package user

import (
	"database/sql"
	"errors"
	"fmt"
	"github.com/go-chi/render"
	"gsc-datasets/internal/db/dao"
	ctype "gsc-datasets/internal/db/dao/ctype"
	pgdb "gsc-datasets/internal/db/sqlc"
	"gsc-datasets/internal/rest/constants"
	appMiddleware "gsc-datasets/internal/rest/middlewares"
	appRender "gsc-datasets/internal/rest/render"
	"gsc-datasets/internal/rest/routers"
	upsms "gsc-datasets/pkg/upstream/scf/sms"
	"gsc-datasets/pkg/util"
	"net/http"
)

type UserRouter struct {
	*routers.RestRouter
}

// New initialization
func New(
	rr *routers.RestRouter,
) *UserRouter {
	s := &UserRouter{
		rr,
	}
	s.register()
	return s
}

func (s *UserRouter) register() {
	userMW := appMiddleware.WithUser(s.RestRouter, constants.UserKey)
	r := s.ChiRouter()
	r.With(appMiddleware.WithPager(s.RestRouter), userMW).Get("/", s.listUsers)
	r.Post("/", s.createUser)
	r.Post("/auth", s.authUser)
	r.With(userMW).Post("/sendcode", s.sendCodeOnUserActiviate)
	r.With(userMW).Post("/signin", s.signInUser)
	r.With(userMW).Put("/nickname", s.updateUserNickname)
	r.With(userMW).Put("/avatar", s.updateUserAvatar)
}

/// 列出user

func (s *UserRouter) listUsers(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	// userPtr := ctx.Value(util.ContextKey("user")).(*pgdb.User)
	pagerPtr := ctx.Value(util.PagerKey{}).(*util.Pager)
	apps, err := s.Dao().ListUsers(ctx, pgdb.ListUsersParams{
		Status: 1,
		Offset: pagerPtr.Offset,
		Limit:  pagerPtr.Limit,
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
	if err := render.Render(w, r, appRender.EntityListToRenderEx(db.MapObjToPtr(apps), s.convUser)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 新建user

func (s *UserRouter) createUser(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	var userPtr *pgdb.User
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		user, err := q.CreateUser(ctx, pgdb.CreateUserParams{
			MockID: fmt.Sprintf("mk%s", util.RandomString(30)),
			Role:   pgdb.EmRoleNormal,
		})
		if err != nil {
			return
		}
		userPtr = &user
		return
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRender(userPtr)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

type authUserReq struct {
	MockID string `json:"mock_id"`
}

func (s *authUserReq) Bind(r *http.Request) error {
	if util.IsEmpty(s.MockID) {
		return errors.New("缺少mock_id字段")
	}
	return nil
}

/// 获取user

func (s *UserRouter) authUser(w http.ResponseWriter, r *http.Request) {
	data := &authUserReq{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	var userPtr *pgdb.User
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {

		user, err := q.GetUserByMockID(ctx, data.MockID)
		if err != nil {
			return
		}
		userPtr = &user
		return
	})
	s.Logger("api/user").Infof("data:%+v", data)
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRenderEx(userPtr, s.convUser)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

type signInUserReq struct {
	UserID int64                `json:"id"`
	Mobile ctype.JsonNullString `json:"mobile,omitempty"`
	Code   string               `json:"code"` // 验证码
}

// type signInUserRequest struct {
// 	*pgdb.ActiviateUserParams
// 	Code string `json:"code"` // 验证码
// }

func (s *signInUserReq) Bind(r *http.Request) error {
	if s.UserID == 0 {
		return errors.New("user缺少id字段")
	}
	if !s.Mobile.Valid {
		return errors.New("user缺少mobile字段") // 人工需要
	}
	if util.IsEmpty(s.Code) {
		return errors.New("缺少code字段")
	}
	return nil
}

// func (s *signInUserRequest) Bind(r *http.Request) error {
// 	if s.ActiviateUserParams == nil {
// 		return errors.New("缺少signInUser字段.")
// 	}
// 	if util.IsEmpty(s.Code) {
// 		return errors.New("缺少code字段.")
// 	}
// 	return nil
// }

/// 注册激活/登录用户

func (s *UserRouter) signInUser(w http.ResponseWriter, r *http.Request) {
	data := &signInUserReq{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	// if userPtr.ID != data.ActiviateUserParams.ID {
	// 	render.Render(w, r, appRender.ErrInvalidRequest("无效的参数"))
	// }
	if userPtr.ID != data.UserID {
		render.Render(w, r, appRender.ErrInvalidRequest("无效的参数"))
	}

	var signInUserPtr *pgdb.User
	user, err := s.Dao().GetUserByMobile(ctx, data.Mobile)
	if err != nil {
		if err == sql.ErrNoRows {
			signInUserPtr = userPtr
		} else {
			render.Render(w, r, appRender.ErrRender(err))
			return
		}
	} else {
		signInUserPtr = &user
	}

	cl := s.SmsClient()
	res, err := cl.VerifyCode(ctx, &upsms.VerifyCodeReq{
		Phone:       data.Mobile.String,
		AppId:       s.GetSmsAppID(userPtr.MockID),
		MessageType: MessageType_ActiviateUser,
		Code:        data.Code,
	})

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if !res.VerifyResult {
		render.Render(w, r, appRender.ErrUpstreamInvokeRender(fmt.Errorf("验证失败:%s", res.Message)))
		return
	}

	if user.Status == 0 {
		err = s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
			user, err := q.ActiviateUser(ctx, pgdb.ActiviateUserParams{
				ID:     signInUserPtr.ID,
				Mobile: signInUserPtr.Mobile,
			})
			if err != nil {
				return
			}
			signInUserPtr = &user
			return
		})
	}

	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRenderEx(signInUserPtr, s.convUser)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

const (
	MessageType_ActiviateUser = "activitate-user"
)

/// 激活用户时发送验证码

type sendCodeOnUserActiviateReq struct {
	Mobile string `json:"mobile"` // 手机号
}

func (s *sendCodeOnUserActiviateReq) Bind(r *http.Request) error {
	if util.IsEmpty(s.Mobile) {
		return errors.New("缺少mobile字段")
	}
	return nil
}

func (s *UserRouter) sendCodeOnUserActiviate(w http.ResponseWriter, r *http.Request) {
	data := &sendCodeOnUserActiviateReq{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)

	cl := s.SmsClient()
	res, err := cl.SendCode(ctx, &upsms.SendCodeReq{
		Phone:       data.Mobile,
		AppId:       s.GetSmsAppID(userPtr.MockID),
		MessageType: MessageType_ActiviateUser,
		RequestId:   s.GetReqID(ctx),
		SignName:    s.GetSmsSignName(),
	})
	s.Logger("api/user").Errorf("err=>%+v", err)
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.StringToRender(res.Message)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 更新用户昵称

type updateUserNicknameReq struct {
	UserID   int64                `json:"id"`
	Nickname ctype.JsonNullString `json:"nickname"` // 昵称
}

func (s *updateUserNicknameReq) Bind(r *http.Request) error {
	if s.UserID == 0 {
		return errors.New("user缺少id字段")
	}
	if !s.Nickname.Valid {
		return errors.New("缺少nickname字段")
	}
	return nil
}

func (s *UserRouter) updateUserNickname(w http.ResponseWriter, r *http.Request) {
	data := &updateUserNicknameReq{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	if userPtr.ID != data.UserID {
		render.Render(w, r, appRender.ErrInvalidRequest("无效的参数"))
	}
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		user, err := q.UpdateUser(ctx, pgdb.UpdateUserParams{
			ID:       data.UserID,
			Nickname: data.Nickname,
		})
		if err != nil {
			return
		}
		userPtr = &user
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRenderEx(userPtr, s.convUser)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}

/// 更新用户头像

type updateUserAvatarReq struct {
	UserID int64                `json:"id"`
	Avatar ctype.JsonNullString `json:"avatar"` // 头像
}

func (s *updateUserAvatarReq) Bind(r *http.Request) error {
	if s.UserID == 0 {
		return errors.New("user缺少id字段")
	}
	if !s.Avatar.Valid {
		return errors.New("缺少avatar字段")
	}
	return nil
}

func (s *UserRouter) updateUserAvatar(w http.ResponseWriter, r *http.Request) {
	data := &updateUserAvatarReq{}
	if err := render.Bind(r, data); err != nil {
		render.Render(w, r, appRender.ErrBadRequest(err))
		return
	}
	ctx := r.Context()
	userPtr := ctx.Value(constants.UserKey).(*pgdb.User)
	if userPtr.ID != data.UserID {
		render.Render(w, r, appRender.ErrInvalidRequest("无效的参数"))
	}
	err := s.TxDao().Exec(ctx, func(q *pgdb.Queries) (err error) {
		user, err := q.UpdateUser(ctx, pgdb.UpdateUserParams{
			ID:     data.UserID,
			Avatar: data.Avatar,
		})
		if err != nil {
			return
		}
		userPtr = &user
		return
	})
	if err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}

	if err := render.Render(w, r, appRender.EntityToRenderEx(userPtr, s.convUser)); err != nil {
		render.Render(w, r, appRender.ErrRender(err))
		return
	}
}
